/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Toepen.LoungeFacade;

import java.net.InetAddress;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.login.LoginException;
import toepen.Deelnemer;
import toepen.Spel;

/**
 * Facade waarmee de GUI communiceert naar de lounge en waarmee de GUI weer
 * geupdate kan worden met nieuwe informatie
 * @author Bart Piggen
 */
public class LoungeFacade extends UnicastRemoteObject implements ILoungeFacade {

    private HashMap<String, ILoungeObserver> _Observers = new HashMap<String, ILoungeObserver>();
    private HashMap<String,LoungeSpel> _Spellen = new HashMap<String, LoungeSpel>();
    private HashMap<String,FacadeGebruiker> _Gebruikers = new HashMap<String, FacadeGebruiker>();
    private MySQLDriver sql;

    /**
     * Constructor voor LoungeFacade
     * @param sql MySQLDriver die benodigd is voor bepaalde database activiteit
     * @throws java.rmi.RemoteException
     */
    public LoungeFacade(MySQLDriver sql) throws RemoteException
    {
        this.sql = sql;
    }

    /**
     * Als een gebruiker vanuit de GUI inlogt wordt met deze functie die login
     * verwerkt, en als deze aanmelding succesvol is een FacadeGebruiker
     * geretourneerd
     * @param naam inlognaam van de gebruiker
     * @param wachtwoord wachtwoord van de gebruiker
     * @return FacadeGebruiker als er succesvol ingelogd is
     * @throws java.rmi.RemoteException
     * @throws javax.security.auth.login.LoginException
     */
    public FacadeGebruiker login(String naam, String wachtwoord) throws RemoteException, LoginException
    {        
        if(!sql.getUsers().validateLogin(naam, wachtwoord))
            throw(new LoginException("Login failed"));
        if(!_Gebruikers.containsKey(naam))
            return(new FacadeGebruiker(naam));
        else
            throw(new LoginException("Deze gebruiker is al aangemeld"));
    }

    /**
     * Als een gebruiker zich aanmeldt gaat deze ook de wijzigingen in de lounge
     * observeren, met deze functie wordt dit geregistreerd
     * @param observer Observer die gebruikt wordt
     * @param gebruiker Gebruiker die observed
     * @throws java.rmi.RemoteException
     */
    public void addObserver(ILoungeObserver observer, FacadeGebruiker gebruiker) throws RemoteException
    {
        if(_Observers.containsKey(gebruiker.getNaam())) {
            return;
        }

        _Observers.put(gebruiker.getNaam(), observer);
        _Gebruikers.put(gebruiker.getNaam(),gebruiker);
      
        notifyObservers();
    }

    /**
     * Het verwijderen van een observer, bijvoorbeeld omdat een gebruiker
     * uitgelogd is. De gebruiker wordt eventuel ook verwijderd.
     * @param gebruiker die verwijderd wordt
     * @throws java.rmi.RemoteException
     */
    public void removeObserver(FacadeGebruiker gebruiker) throws RemoteException
    {
        if(!_Observers.containsKey(gebruiker.getNaam()))
        {
            return;
        }

        _Observers.remove(gebruiker.getNaam());
        _Gebruikers.remove(gebruiker.getNaam());
        
        Iterator iterator = _Spellen.keySet().iterator();
        while(iterator.hasNext())
        {
            LoungeSpel spel = _Spellen.get(iterator.next());
            if(spel.getSpelStarter().getNaam().equals(gebruiker.getNaam()))
            {
                iterator.remove();
            }else
            {
                spel.removeDeelnemer(gebruiker);
            }
        }

        notifyObservers();
    }

    /**
     * Functie voor het registreren van een nieuwe gebruiker
     * @param naam gewenste naam van de gebruiker
     * @param gebruikersnaam gewenste gebruikersnaam van de gebruiker
     * @param wachtwoord gewenste wachtwoord van de gebruiker
     * @return true als het aanmelden gelukt is
     * @throws java.rmi.RemoteException
     */
    public boolean registreerGebruiker(String naam, String gebruikersnaam, String wachtwoord) throws RemoteException {
        Boolean blRegistratieGelukt = false;
        try {
            blRegistratieGelukt = sql.getUsers().createUser(gebruikersnaam, wachtwoord, naam);
        } catch (Exception e) {
             throw new RemoteException("SQL Error");
    }
        if (!blRegistratieGelukt){
            throw new RemoteException("Registratie mislukt!");
        }
        return blRegistratieGelukt;
    }

    /**
     * Functie voor het aanmaken van een nieuw spel door een gebruiker als deze
     * gebruiker nog geen spel aangemaakt heeft
     * @param host IP-adres van de gebruiker
     * @param naam naam van het spel
     * @param aantalSpelers aantal spelers wat mee doet
     * @param spelStarter FacadeGebruiker die het spel gestart heeft
     * @param enableBlindKloppen boolean of regel blind kloppen gebruikt wordt
     * @param enableKansOpToep boolean of regel blind kloppen gebruikt wordt
     * @param enableKoningsToepen boolean of regel koningstoepen gebruikt wordt
     * @param enableVijfKruizen boolean of regel vijf kruizen gebruikt wordt
     * @param enableVuileWas boolean of regel vuile was gebruikt wordt
     * @throws java.rmi.RemoteException boolean of regel blind kloppen gebruikt wordt
     */
    public void maakSpelAan(String host, String naam, int aantalSpelers, FacadeGebruiker spelStarter, boolean enableBlindKloppen, boolean enableKansOpToep, boolean enableKoningsToepen, boolean enableVijfKruizen, boolean enableVuileWas) throws RemoteException {
        Iterator iterator = _Spellen.keySet().iterator();
        Boolean blnBestaatAl = false;
        while(iterator.hasNext())
        {
            LoungeSpel spel = _Spellen.get(iterator.next());
            if(spel.getNaam().equals(naam))
            {
                blnBestaatAl = true;
            }
        }
        if (!blnBestaatAl){
            _Spellen.put(naam,new LoungeSpel(host, 1, naam, spelStarter, aantalSpelers, 0));
        }
        else
        {
            throw new RemoteException("Dubbel spel kan niet.");
        }
        notifyObservers();
    }

    /**
     * Functie voor het meedoen aan een spel door een gebruiker
     * @param spel LoungeSpel waaraan de gebruiker mee wil doen
     * @param gebruiker die mee wil doen
     * @throws java.rmi.RemoteException
     */
    public void joinSpel(LoungeSpel spel, FacadeGebruiker gebruiker) throws RemoteException
    {
        LoungeSpel spel_=_Spellen.get(spel.getNaam());

        spel_.addDeelnemer(gebruiker);
        notifyObservers();
    }

    /**
     * Start het spel op
     * @param spel wat opgestart dient te worden
     * @throws java.rmi.RemoteException
     */
    public void startSpel(LoungeSpel spel) throws RemoteException {
        LoungeSpel spel_=_Spellen.get(spel.getNaam());

        if(spel_.getAangemeld()>1) {
            ArrayList<FacadeGebruiker> a=new ArrayList<FacadeGebruiker>();
            FacadeGebruiker[] deelnemers=spel_.getDeelnemers();
            for(int i=0;i<deelnemers.length;i++)
                a.add(deelnemers[i]);
            Iterator<FacadeGebruiker> iterator=a.iterator();

            // First start the game of the spelstarter
            ILoungeObserver o=_Observers.get(spel_.getSpelStarter().getNaam());
            o.start(spel_, this);
            // Then start the deelnemers
            while(iterator.hasNext()) {
                FacadeGebruiker gebruiker=iterator.next();
                _Observers.get(gebruiker.getNaam()).start(spel_, this);
            }
        }

        notifyObservers();
    }

    /**
     * Als een spel gestopt wordt moet het ook gestopt worden in de
     * LoungeFacade en de observers daarvan op de hoogte gebracht worden
     * @param _LoungeSpel wat gestopt wordt
     */
    public void stopSpel(LoungeSpel _LoungeSpel) {
        _Spellen.remove(_LoungeSpel.getNaam());
        notifyObservers();
        return;
    }

    /**
     * Functie die alle observers op de hoogte brengt met de nieuwe ovservable
     */
    private void notifyObservers()
    {
        // Create the observable object
        LoungeObservable observable=new LoungeObservable(new ArrayList<LoungeSpel>(_Spellen.values()),new ArrayList<FacadeGebruiker>(_Gebruikers.values()));

        Iterator<ILoungeObserver> iterator=_Observers.values().iterator();

        while(iterator.hasNext())
        {
            try
            {
                iterator.next().update(observable);
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }

}
